`include "../include/cpu_defines.sv"

module ROB#(
	parameter BANK_NUM=2,
	parameter HALF_SIZE=(`ROB_SIZE>>1)
)(
	input logic cpu_clk,
	input logic cpu_rst_n,
	input logic stall,
	input logic clear,
	input logic inst1_en,
	input logic inst2_en,
	input logic inst1_branch,
	input logic inst2_branch,
	input logic inst1_rasup,
	input logic inst1_rasdown,
	input logic inst2_rasup,
	input logic inst2_rasdown,
	input logic inst1_ll,
	input logic inst2_ll,
	input logic inst1_entryhi,
	input logic inst2_entryhi,
	input logic inst1_tlb,
	input logic inst2_tlb,
	input logic inst1_place2,
	input logic inst2_place2,
	input logic inst1_fetchexc,
	input logic inst2_fetchexc,
	input logic [31: 0] inst_addr1,
	input logic [31: 0] inst_addr2,
	input logic [`INST_TYPE_BUS] inst1_type,
	input logic [`INST_TYPE_BUS] inst2_type,
	input logic [5: 0] inst1_rd,
	input logic [5: 0] prf_inst1_rd,
	input logic [5: 0] inst2_rd,
	input logic [5: 0] prf_inst2_rd,
	input logic wb_en1,
	input logic wb_en2,
	input logic wb_en3,
	input logic [5: 0] wb_rob_index1,
	input logic [5: 0] wb_rob_index2,
	input logic [5: 0] wb_rob_index3,
	input logic [`EXC_CODE_BUS] wb_exccode1,
	input logic [`EXC_CODE_BUS] wb_exccode2,
	input logic [`EXC_CODE_BUS] wb_exccode3,
	input logic inst1_branch_fail,
	input logic inst2_branch_fail,
	// input logic branch_exception,
	// input logic [5: 0] branch_exc_rob_index,

`ifdef CPU_DEBUG
	input logic [31:0] debug_rob_inst1_in, 
	input logic [31:0] debug_rob_iaddr1_in,
	input logic [31:0] debug_rob_inst2_in,
	input logic [31:0] debug_rob_iaddr2_in,

	input logic [31:0] 	debug_rob_wdata1_in,
	input logic [31:0] 	debug_rob_wdata2_in,	
	input logic [31: 0] debug_rob_wdata3_in,
	output logic [31: 0]  			debug_retire1_pc,
	output logic [3: 0]           	debug_retire1_rf_wen,
	output logic [4: 0] 		  	debug_retire1_rf_wnum,
	output logic [31: 0]          	debug_retire1_rf_wdata,
	output logic [31: 0] 			debug_retire2_pc,
	output logic [3: 0] 			debug_retire2_rf_wen,
	output logic [4: 0] 			debug_retire2_rf_wnum,
	output logic [31: 0] 			debug_retire2_rf_wdata,
	output logic 					debug_commit1,
	output logic 					debug_commit2,
	input logic 					debug_int,
`endif
	input logic stop_retire,
	output logic out_rob_en1,
	output logic out_rob_en2,
	output logic out_rob_cp0_en1,
	output logic out_rob_cp0_en2,
	output logic [`INST_TYPE_BUS] out_inst1_type,
	output logic [`INST_TYPE_BUS] out_inst2_type,
	output logic out_branch_fail1,
	output logic out_branch_fail2,
	output logic [`EXC_CODE_BUS] out_exccode1,
	output logic [`EXC_CODE_BUS] out_exccode2,
	output logic out_isds1,
	output logic out_isds2,
	output logic out_branchds1,
	output logic out_branchds2,
	output logic out_rasup1,
	output logic out_rasup2,
	output logic out_rasdown1,
	output logic out_rasdown2,
	output logic out_ll1,
	output logic out_ll2,
	output logic out_entryhi1,
	output logic out_entryhi2,
	output logic [31: 0] out_pc1,
	output logic [31: 0] out_pc2,
	(* mark_debug="true" *)
	output logic [5 :0] out_rob_index1,
	output logic [5: 0] out_rob_index2,
	output logic [5: 0] rob_index1,
	output logic [5: 0] rob_index2,
	output logic [5: 0] rob_origin_rd1,
	output logic [5: 0] rob_origin_rd2,
	output logic [5: 0] wb_origin_rd1,
	output logic [5: 0] wb_origin_rd2,
	output logic [5: 0] rob_rd1,
	output logic [5: 0] rob_rd2,
	output logic flush_request,
	output logic direction1,
	output logic direction2,
	output logic out_rob_en1_pre,
	output logic out_rob_en2_pre,
	output logic out_cp0_en1_pre,
	output logic out_cp0_en2_pre,
	output logic out_fetchexc1,
	output logic out_fetchexc2
);
	// typedef struct packed {
	// 	(* MAX_FANOUT=300 *)
	// 	logic ready, branch_fail;
	// 	logic [`INST_TYPE_BUS] _type;
	// 	logic [5: 0] origin_rd;
	// 	logic [5: 0] rd;
		
	// 	`ifdef ROB_DEBUG
	// 	logic [31:0] inst, iaddr, wdata;
	// 	`endif 
	// } rob_element_t;
	// (* MAX_FANOUT=300 *)
	logic [$clog2(HALF_SIZE)-1: 0] head, next_head, next_headp1;
	// (* MAX_FANOUT=300 *)
	logic [$clog2(HALF_SIZE)-1: 0] tail;
	logic [$clog2(HALF_SIZE)-1: 0] next_tail;
	logic [5: 0] headp1;
	logic [$clog2(HALF_SIZE): 0] remain_num;
	logic add_head_num, add_num;
	logic direction;
	logic next_ds, next_branch_ds; // delay slot
	logic next_bank1_en;


	// addr: 32, _type0: 1, rd0: 6, _type1, rd1
	// only need write on dis stage and read on retire stage
	typedef struct packed {
		logic [31: 0] addr1;
		logic [31: 0] addr2;
		logic [2: 0] _type0;
		logic [5: 0] rd0;
		logic [2: 0] _type1;
		logic [5: 0] rd1;
		logic isds1;
		logic isds2;
		logic branchds1;
		logic branchds2;
		logic inst1_rasup; // jal jalr bgezal bltzal
		logic inst2_rasup;
		logic inst1_rasdown;
		logic inst2_rasdown;
		logic inst1_ll;
		logic inst2_ll;
		logic inst1_entryhi;
		logic inst2_entryhi;
		logic inst1_tlb;
		logic inst2_tlb;
		logic inst1_place2;
		logic inst2_place2;
		logic inst1_fetchexc;
		logic inst2_fetchexc;
	} bank_common_element_t;
	bank_common_element_t out_element;
	SDPRAM #(
		.WORD_WIDTH(100),
		.DEPTH(HALF_SIZE),
		// .MEMORY_PRIMITIVE("block"),
		.ADDR_WIDTH($clog2(HALF_SIZE)),
		.READ_LATENCY(1)
	) bank_common(
		.clk(cpu_clk),
		.ena(inst1_en),
		.wea((inst1_en & ~stall)),
		.addra(tail),
		.dina({inst_addr1, inst_addr2, inst1_type, prf_inst1_rd, inst2_type, prf_inst2_rd, next_ds, inst1_branch && inst2_en, next_branch_ds, inst1_type == `INST_TYPE_BRANCH && inst2_en, inst1_rasup, inst2_en & inst2_rasup, inst1_rasdown, inst2_en & inst2_rasdown, inst1_ll, inst2_ll, inst1_entryhi, inst2_entryhi, inst1_tlb, inst2_tlb, inst1_place2, inst2_place2, inst1_fetchexc, inst2_fetchexc}),
		.enb(1'b1),
		.addrb(next_head),
		.doutb(out_element)
	);
	// need write on dis stage and wb stage, read on retire stage
	logic [HALF_SIZE-1: 0] bank0_ready;
	logic [HALF_SIZE-1: 0] bank1_ready;
	logic [HALF_SIZE-1: 0] bank0_fail; // 对应延迟槽指令
	logic [HALF_SIZE-1: 0] bank1_fail;
	logic [HALF_SIZE-1: 0] bank1_en;
	// write on dis stage , read on wb stage and retire stage
	logic [5: 0] bank0_origin_rd[HALF_SIZE-1: 0];
	logic [5: 0] bank1_origin_rd[HALF_SIZE-1: 0];
	logic [`EXC_CODE_BUS] bank0_exccode[HALF_SIZE-1: 0];
	logic [`EXC_CODE_BUS] bank1_exccode[HALF_SIZE-1: 0];
	(* mark_debug="true" *)
	logic next_bank0_ready;
	logic next_bank0_fail;
	(* mark_debug="true" *)
	logic next_bank1_ready;
	logic next_bank1_fail;
	logic [5: 0] next_bank0_origin_rd;
	logic [5: 0] next_bank1_origin_rd;
	logic [5: 0] wb_rob_indexp1, wb_rob_indexp2, wb_rob_indexp3, branch_exc_indexp1;
	logic bank0_retire;

	// rob_element_t buffer[`ROB_SIZE-1: 0];

	assign add_num = inst1_en;
	assign add_head_num = out_rob_en2 ;
	assign flush_request = remain_num < 2;
	assign rob_index1 = tail << 1;
	assign rob_index2 = (tail << 1) + 1;
	// assign branch_exc_indexp1 = branch_exc_rob_index + 1;
	// assign head_element = buffer[head];
	// assign headp1_element = buffer[headp1];
	// 分支预测失败之后rob将会清空，所以如果分支预测的延迟槽指令时mult时需要等待mult的两部分完成
	// assign out_rob_en1 = next_bank0_ready & (out_exccode1 == `EXC_NONE) & ((next_bank0_fail & (out_element._type0 != `INST_TYPE_MULT)) | next_bank1_ready) & ~clear;
	// assign out_rob_en2 = next_bank0_ready & ~out_element.inst1_tlb & (out_exccode1 == `EXC_NONE && out_exccode2 == `EXC_NONE) & (~next_bank0_fail | next_bank0_fail & (out_element._type0 == `INST_TYPE_MULT)) & next_bank1_ready & ~clear;
	assign out_rob_en1_pre = next_bank0_ready & (out_exccode1 == `EXC_NONE) & ~clear & ~bank0_retire &
					(~next_bank0_fail | next_bank0_fail & ((~out_element.inst1_place2) | next_bank1_ready));
	assign out_rob_en2_pre = next_bank0_ready & next_bank1_ready & (out_exccode1 == `EXC_NONE && out_exccode2 == `EXC_NONE) & ~clear & (~next_bank0_fail | next_bank0_fail & out_element.inst1_place2);
	assign out_cp0_en1_pre = next_bank0_ready & (out_exccode1 != `EXC_NONE) & ~clear;
	assign out_cp0_en2_pre = next_bank0_ready & next_bank1_ready & (out_exccode1 == `EXC_NONE) & (out_exccode2 != `EXC_NONE) & (~(next_bank0_fail & (!out_element.inst1_place2))) & ~clear;
	assign out_rob_en1 = ~stop_retire & out_rob_en1_pre;
	assign out_rob_en2 = ~stop_retire & out_rob_en2_pre;
	assign out_rob_cp0_en1 = ~stop_retire & out_cp0_en1_pre;
	assign out_rob_cp0_en2 = ~stop_retire & out_cp0_en2_pre;
	assign out_inst1_type = out_element._type0;
	assign out_inst2_type = next_bank1_en ? out_element._type1 : out_element._type0;
	assign out_branch_fail1 = (next_bank0_fail & (!out_element.inst1_place2));
	assign out_branch_fail2 = (next_bank0_fail & (out_element.inst1_place2) | next_bank1_fail);
	assign out_isds1 = out_element.isds1;
	assign out_isds2 = out_element.isds2;
	assign out_branchds1 = out_element.branchds1;
	assign out_branchds2 = out_element.branchds2;
	assign out_rasup1 = out_rob_en1 & out_element.inst1_rasup;
	assign out_rasup2 = out_rob_en2 & out_element.inst2_rasup;
	assign out_rasdown1 = out_rob_en1 & out_element.inst1_rasdown;
	assign out_rasdown2 = out_rob_en2 & out_element.inst2_rasdown;
	assign out_ll1 = out_rob_en1 & out_element.inst1_ll;
	assign out_ll2 = out_rob_en2 & out_element.inst2_ll;
	assign out_entryhi1 = out_element.inst1_entryhi;
	assign out_entryhi2 = out_element.inst2_entryhi;
	assign out_fetchexc1 = out_element.inst1_fetchexc;
	assign out_fetchexc2 = out_element.inst2_fetchexc;
	assign out_pc1 = out_element.addr1;
	assign out_pc2 = out_element.addr2;
	assign out_rob_index1 = head << 1;
	assign headp1 = (head << 1) + 1;
	assign out_rob_index2 = headp1;
	assign rob_origin_rd1 = out_rob_en1 ? next_bank0_origin_rd : 0;
	assign rob_rd1 = out_rob_en1 ? out_element.rd0 : 0;
	assign rob_origin_rd2 = out_rob_en2 ? next_bank1_origin_rd : 0;
	assign rob_rd2 = out_rob_en2 ? out_element.rd1 : 0;
	assign wb_origin_rd1 = wb_rob_index1[0] ? bank1_origin_rd[wb_rob_index1[5: 1]] : bank0_origin_rd[wb_rob_index1[5: 1]];
	assign wb_origin_rd2 = wb_rob_index2[0] ? bank1_origin_rd[wb_rob_index2[5: 1]] : bank0_origin_rd[wb_rob_index2[5: 1]];
	assign wb_rob_indexp1 = wb_rob_index1 + (~bank1_en[wb_rob_index1[5 :1]] ? 2 : 1);
	assign wb_rob_indexp2 = wb_rob_index2 + (~bank1_en[wb_rob_index2[5 :1]] ? 2 : 1);
	assign wb_rob_indexp3 = wb_rob_index3 + (~bank1_en[wb_rob_index3[5 :1]] ? 2 : 1);
	assign next_tail = tail + (add_num & ~stall);
	assign next_head = head + add_head_num;
	assign direction1 = direction;
	assign direction2 = direction;

	always_ff @(posedge cpu_clk)begin
		if(cpu_rst_n == 1'b0 || clear)begin
			head <= 0;
			tail <= 0;
			remain_num <= 7'b0100000;
			direction <= 0;
			for(int i=0; i<HALF_SIZE; i++)begin
				bank0_ready[i] <= 0;
				bank1_ready[i] <= 0;
				bank0_fail[i] <= 0;
				bank1_fail[i] <= 0;
				bank0_origin_rd[i] <= 0;
				bank1_origin_rd[i] <= 0;
				bank0_exccode[i] <= `EXC_NONE;
				bank1_exccode[i] <= `EXC_NONE;
			end
			next_bank0_origin_rd <= 0;
			next_bank1_origin_rd <= 0;
			next_bank0_ready <= 0;
			next_bank1_ready <= 0;
			next_bank0_fail <= 0;
			next_bank1_fail <= 0;
			next_ds <= 0;
			next_branch_ds <= 0;
			out_exccode1 <= `EXC_NONE;
			out_exccode2 <= `EXC_NONE;
			bank1_en <= 0;
			bank0_retire <= 0;
			next_bank1_en <= 0;
		end
		else begin
			remain_num <= remain_num + add_head_num - {~stall & add_num};
			head <= head + add_head_num;
			tail <= next_tail;
			next_bank0_ready <= bank0_ready[next_head];
			next_bank1_ready <= bank1_ready[next_head];
			next_bank0_fail <= bank0_fail[next_head];
			next_bank1_fail <= bank1_fail[next_head];
			next_bank0_origin_rd <= bank0_origin_rd[next_head];
			next_bank1_origin_rd <= bank1_origin_rd[next_head];
			out_exccode1 <= bank0_exccode[next_head];
			out_exccode2 <= bank1_exccode[next_head]; 
			bank0_retire <= add_head_num ? 1'b0 : out_rob_en1 ? 1'b1 : bank0_retire;
			next_bank1_en <= bank1_en[next_head];

			bank0_ready <= (bank0_ready | 
			{HALF_SIZE{wb_en1 & ~wb_rob_index1[0]}} & (32'b1 << (wb_rob_index1[5: 1])) | 
			{HALF_SIZE{wb_en2 & ~wb_rob_index2[0]}} & (32'b1 << (wb_rob_index2[5: 1])) |
			{HALF_SIZE{wb_en3 & ~wb_rob_index3[0]}} & (32'b1 << (wb_rob_index3[5: 1]))) & 
			(~({HALF_SIZE{add_head_num}} & (32'b1 << head)));
			bank1_ready <= (bank1_ready | 
			{HALF_SIZE{wb_en1 & wb_rob_index1[0]}} & (32'b1 << (wb_rob_index1[5: 1])) | 
			{HALF_SIZE{wb_en2 & wb_rob_index2[0]}} & (32'b1 << (wb_rob_index2[5: 1])) |
			{HALF_SIZE{wb_en3 & wb_rob_index3[0]}} & (32'b1 << (wb_rob_index3[5: 1]))) & 
			(~({HALF_SIZE{add_head_num}} & (32'b1 << head)));
			bank0_fail <= (bank0_fail | 
			{HALF_SIZE{wb_en1 & inst1_branch_fail & ~wb_rob_indexp1[0]}} & (32'b1 << (wb_rob_indexp1[5: 1])) | 
			{HALF_SIZE{wb_en2 & inst2_branch_fail & ~wb_rob_indexp2[0]}} & (32'b1 << (wb_rob_indexp2[5: 1]))) & 
			(~({HALF_SIZE{add_head_num}} & (32'b1 << head)));
			bank1_fail <= (bank1_fail | 
			{HALF_SIZE{wb_en1 & inst1_branch_fail & wb_rob_indexp1[0]}} & (32'b1 << (wb_rob_indexp1[5: 1])) | 
			{HALF_SIZE{wb_en2 & inst2_branch_fail & wb_rob_indexp2[0]}} & (32'b1 << (wb_rob_indexp2[5: 1]))) & 
			(~({HALF_SIZE{add_head_num}} & (32'b1 << head)));

			// if(branch_exception & ~branch_exc_indexp1[0])begin
			// 	bank0_exccode[branch_exc_indexp1[5: 1]] <= `EXC_AdEL;
			// end
			// if(branch_exception & branch_exc_indexp1[0])begin
			// 	bank1_exccode[branch_exc_indexp1[5: 1]] <= `EXC_AdEL;
			// end

			if(wb_en1 & ~wb_rob_index1[0] && wb_exccode1 != `EXC_NONE)begin
				bank0_exccode[wb_rob_index1[5: 1]] <= wb_exccode1;
			end
			if(wb_en1 & wb_rob_index1[0] && wb_exccode1 != `EXC_NONE)begin
				bank1_exccode[wb_rob_index1[5: 1]] <= wb_exccode1;
			end
			if(wb_en2 & ~wb_rob_index2[0] && wb_exccode2 != `EXC_NONE)begin
				bank0_exccode[wb_rob_index2[5: 1]] <= wb_exccode2;
			end
			if(wb_en2 & wb_rob_index2[0] && wb_exccode2 != `EXC_NONE)begin
				bank1_exccode[wb_rob_index2[5: 1]] <= wb_exccode2;
			end
			if(wb_en3 & ~wb_rob_index3[0] && wb_exccode3 != `EXC_NONE)begin
				bank0_exccode[wb_rob_index3[5: 1]] <= wb_exccode3;
			end
			if(wb_en3 & wb_rob_index3[0] && wb_exccode3 != `EXC_NONE)begin
				bank1_exccode[wb_rob_index3[5: 1]] <= wb_exccode3;
			end

			if(tail == 5'b11111 && add_num && ~stall)begin
				direction <= ~direction;
			end

			if(inst1_en & ~stall)begin
				bank0_origin_rd[tail] <= inst1_rd;
				bank1_origin_rd[tail] <= inst2_rd;
				bank1_en[tail] <= inst2_en;
				next_ds <= inst2_en && inst2_branch || inst1_branch && !inst2_en;
				next_branch_ds <= inst2_en && inst2_type == `INST_TYPE_BRANCH || inst1_type == `INST_TYPE_BRANCH && !inst2_en;
			end
		end
	end


`ifdef CPU_DEBUG
	logic [31: 0] debug_bank0_data[HALF_SIZE-1: 0];
	logic [31: 0] debug_bank1_data[HALF_SIZE-1: 0];
	logic [31: 0] next_debug_data1, next_debug_data2;
	always_ff @(posedge cpu_clk)begin
		if(cpu_rst_n == 1'b0 || clear)begin
			for(int i=0; i<HALF_SIZE; i++)begin
				debug_bank0_data[i] <= 0;
				debug_bank1_data[i] <= 0;
			end
			next_debug_data1 <= 0;
			next_debug_data2 <= 0;
		end
		else begin
			next_debug_data1 <= debug_bank0_data[next_head];
			next_debug_data2 <= debug_bank1_data[next_head];
			if(add_head_num)begin
				debug_bank0_data[head] <= 0;
				debug_bank1_data[head] <= 0;
			end
			if(wb_en1 & ~wb_rob_index1[0])begin
				debug_bank0_data[wb_rob_index1[5: 1]] <= debug_rob_wdata1_in;
			end
			if(wb_en1 & wb_rob_index1[0])begin
				debug_bank1_data[wb_rob_index1[5: 1]] <= debug_rob_wdata1_in;
			end
			if(wb_en2 & ~wb_rob_index2[0])begin
				debug_bank0_data[wb_rob_index2[5: 1]] <= debug_rob_wdata2_in;
			end
			if(wb_en2 & wb_rob_index2[0])begin
				debug_bank1_data[wb_rob_index2[5: 1]] <= debug_rob_wdata2_in;
			end
			if(wb_en3 & ~wb_rob_index3[0])begin
				debug_bank0_data[wb_rob_index3[5: 1]] <= debug_rob_wdata3_in;
			end
			if(wb_en3 & wb_rob_index3[0])begin
				debug_bank1_data[wb_rob_index3[5: 1]] <= debug_rob_wdata3_in;
			end			
		end
		debug_commit1 <= out_rob_en1 || out_rob_cp0_en1 || debug_int;
		debug_commit2 <= out_rob_en2 && next_bank1_en && !out_element.inst1_place2 || out_rob_cp0_en2;
		//如果是乘除法的话wen = 0
		if(out_rob_en1 || out_rob_cp0_en1 || debug_int)begin
			debug_retire1_pc <= out_element.addr1;
			debug_retire1_rf_wen <= (next_bank0_origin_rd && (!out_element.inst1_place2) && next_bank0_origin_rd != 6'h20 && next_bank0_origin_rd != 6'h21) ? 1 : 0;
			debug_retire1_rf_wnum <= next_bank0_origin_rd;
			debug_retire1_rf_wdata <= debug_bank0_data[head];
		end
		else begin
			debug_retire1_pc <= 0;
			debug_retire1_rf_wen <= 0;
			debug_retire1_rf_wnum <= 0;
			debug_retire1_rf_wdata <= 0;
		end
		if(out_rob_en2 && next_bank1_en || out_rob_cp0_en2)begin
			debug_retire2_pc <= out_element.addr2;
			debug_retire2_rf_wen <= (next_bank1_origin_rd && (!out_element.inst1_place2) && next_bank1_origin_rd != 6'h20 && next_bank1_origin_rd != 6'h21) ? 1 : 0;
			debug_retire2_rf_wnum <= next_bank1_origin_rd;
			debug_retire2_rf_wdata <= debug_bank1_data[head];
		end
		else begin
			debug_retire2_pc <= 0;
			debug_retire2_rf_wen <= 0;
			debug_retire2_rf_wnum <= 0;
			debug_retire2_rf_wdata <= 0;
		end
	end
`endif
endmodule